package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.zzyl.base.PageResponse;
import com.zzyl.config.WebSocketServer;
import com.zzyl.constant.CacheConstants;
import com.zzyl.dto.AlertNotifyDto;
import com.zzyl.dto.AlertRuleDto;
import com.zzyl.entity.AlertData;
import com.zzyl.entity.AlertRule;
import com.zzyl.mapper.AlertDataMapper;
import com.zzyl.mapper.AlertRuleMapper;
import com.zzyl.mapper.DeviceMapper;
import com.zzyl.mapper.UserRoleMapper;
import com.zzyl.service.AlertRuleService;
import com.zzyl.utils.StringUtils;
import com.zzyl.vo.AlertRuleVo;
import com.zzyl.vo.DeviceDataVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


@Service
public class AlertRuleServiceImpl implements AlertRuleService {

    @Autowired
    private AlertRuleMapper alertRuleMapper;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private AlertDataMapper alertDataMapper;

    @Autowired
    private WebSocketServer webSocketServer;

    //设备维护人员的角色名称
    @Value("${zzyl.alert.deviceMaintainerRole}")
    private String deviceMaintainerRole;
    //超级管理员的角色名称
    @Value("${zzyl.alert.managerRole}")
    private String managerRole;


    @Override
    public void create(AlertRuleDto alertRuleDto) {
        AlertRule alertRule = BeanUtil.toBean(alertRuleDto, AlertRule.class);
        alertRuleMapper.create(alertRule);
    }

    @Override
    public PageResponse<AlertRuleVo> getPage(Integer pageNum, Integer pageSize, String alertRuleName, String functionName, String productKey) {
        PageHelper.startPage(pageNum, pageSize);
        Page<AlertRuleVo> alertRuleVos = alertRuleMapper.getPage(alertRuleName, functionName, productKey);
        alertRuleVos.getResult().forEach(alertRuleVo -> {
            String rule = CharSequenceUtil.format("{}{}{},持续{}个周期就报警",
                    alertRuleVo.getFunctionName(), alertRuleVo.getOperator(), alertRuleVo.getValue(), alertRuleVo.getDuration());
            alertRuleVo.setRules(rule);
        });
        return PageResponse.of(alertRuleVos, AlertRuleVo.class);
    }

    @Override
    public AlertRuleVo readById(Long id) {
        AlertRule alertRule = alertRuleMapper.readById(id);
        AlertRuleVo alertRuleVo = BeanUtil.toBean(alertRule, AlertRuleVo.class);
        return alertRuleVo;
    }

    @Override
    public void updateById(AlertRuleDto alertRuleDto) {
        AlertRule alertRule = BeanUtil.toBean(alertRuleDto, AlertRule.class);
        alertRuleMapper.updateById(alertRule);
    }

    @Override
    public void deleteById(Long id) {
        alertRuleMapper.deleteById(id);
    }

    @Override
    public void updateStatus(Long id, Integer status) {
        alertRuleMapper.updateStatus(id, status);
    }

    @Override
    public void alertFilter(DeviceDataVo deviceDataVo) {
        // 获取所有设备上报数据时间
        LocalDateTime alarmTime = deviceDataVo.getAlarmTime();
        // 获取时间差值
        long between = LocalDateTimeUtil.between(alarmTime, LocalDateTime.now(), ChronoUnit.SECONDS);
        // 时间差值大于60秒，则不进行报警
        if (between > 60) {
            return;
        }

        // 获取所有报警规则
        List<AlertRule> alertRules1 = alertRuleMapper.selectByFunctionId(deviceDataVo.getFunctionId(), deviceDataVo.getIotId(), deviceDataVo.getProductKey());
        List<AlertRule> alertRules2 = alertRuleMapper.selectByFunctionId(deviceDataVo.getFunctionId(), "-1", deviceDataVo.getProductKey());
        // 合并
        Collection<AlertRule> alertRules0 = CollUtil.addAll(alertRules1, alertRules2);

        // 判断是否有报警规则
        if (CollUtil.isEmpty(alertRules0)) {
            return;
        }

        // 如果不报空，则将所有报警规则进行遍历校验
        alertRules0.forEach(alertRule -> {
            deviceDateAlarmHandler(deviceDataVo, alertRule);
        });
    }

    private void deviceDateAlarmHandler(DeviceDataVo deviceDataVo, AlertRule alertRule) {
        // 第一层校验，判断是否在生效时间内
        String[] split = alertRule.getAlertEffectivePeriod().split("~");
        LocalTime start = LocalTime.parse(split[0]);
        LocalTime end = LocalTime.parse(split[1]);
        // 获取上报时间
        LocalTime localTime = deviceDataVo.getAlarmTime().toLocalTime();
        // 判断是否在生效时间内,不在则不进行报警
        if (localTime.isBefore(start) || localTime.isAfter(end)) {
            return;
        }

        // 获取设备id
        String iotId = deviceDataVo.getIotId();
        // 从redis中获取对应数据
        String aggCountKey = CacheConstants.ALERT_TRIGGER_COUNT_PREFIX + iotId + ":" + deviceDataVo.getFunctionId() + ":" + alertRule.getId();

        // 第二层校验，判断数据是否达到阈值
        int compare = NumberUtil.compare(Float.parseFloat(deviceDataVo.getDataValue()), alertRule.getValue());
        if ((compare < 0 && alertRule.getOperator().equals("<")) || (compare >= 0 && alertRule.getOperator().equals(">="))) {
            // 符合规则报警
        } else { // 不符合规则报警,删除异常数据
            redisTemplate.delete(aggCountKey);
            return;
        }


        // 第三层校验，判断是否处于沉默周期
        String aggSilentKey = CacheConstants.ALERT_SILENT_PREFIX + iotId + ":" + deviceDataVo.getFunctionId() + ":" + alertRule.getId();
        String slientData = redisTemplate.opsForValue().get(aggSilentKey);
        if (!StringUtils.isEmpty(slientData)) {
            return;
        }

        // 第四层校验，判断是否达到持续周期
        // 4.1 查询报警次数
        String aggCountData = redisTemplate.opsForValue().get(aggCountKey);
        // 如果不存在，则创建,如果已存在，则进行累加
        int count = ObjectUtil.isEmpty(aggCountData) ? 1 : (Integer.parseInt(aggCountData) + 1);

        // 4.2 判断是否达到持续周期
        if (ObjectUtil.notEqual(count, alertRule.getDuration())) {
            // 未达到持续周期，则进行累加
            redisTemplate.opsForValue().set(aggCountKey, count + "");
            return;
        }

        // 4.3 达到持续周期，则进行报警
        // 4.3.1 删除缓存
        redisTemplate.delete(aggCountKey);
        // 4.3.2 添加沉默周期
        redisTemplate.opsForValue().set(aggSilentKey, deviceDataVo.getDataValue(), alertRule.getAlertSilentPeriod(), TimeUnit.SECONDS);
        // 4.3.3 发送报警消息
        List<Long> userIds = null;
        if (alertRule.getAlertDataType() == 0) { // 如果为0，则为老人的报警数据
            if (deviceDataVo.getLocationType() == 0) { // 如果为0，则为老人
                userIds = deviceMapper.selectNursingIdsByIotIdWithElder(iotId);
            } else if (deviceDataVo.getLocationType() == 1 && deviceDataVo.getPhysicalLocationType() == 2) {// 此时为床
                userIds = deviceMapper.selectNursingIdsByIotIdWithBed(iotId);
            }
        } else {
            // 获取所有维修员
            userIds = deviceMapper.selectUserIdsByRoleName(deviceMaintainerRole);
        }

        // 获取所有管理员id
        List<Long> managerIds =deviceMapper.selectUserIdsByRoleName(managerRole);
        // 合并
        List<Long> allConsumerIds = CollUtil.addAllIfNotContains(userIds, managerIds);
        allConsumerIds = CollUtil.distinct(allConsumerIds);

        // 新增报警数据
        List<AlertData> alertData = insertAlertData(deviceDataVo, alertRule, allConsumerIds);

        // 获取需要推送的用户id列表
        List<Long> userIdList = alertData.stream().map(AlertData::getUserId).collect(Collectors.toList());

        // 发送websocket消息
        websocketNotify(alertRule, alertData.get(0), userIds);

    }

    private void websocketNotify(AlertRule alertRule, AlertData alertData, List<Long> userIds) {
        // websocket推送消息
        AlertNotifyDto alertNotifyDto = BeanUtil.toBean(alertData, AlertNotifyDto.class);
        alertNotifyDto.setFunctionName(alertRule.getFunctionName());
        alertNotifyDto.setAlertDataType(alertData.getType());
        alertNotifyDto.setNotifyType(1);

        // 给指定消费者发送消息
        webSocketServer.sendMessageToConsumer(alertNotifyDto, userIds);
    }

    private List<AlertData> insertAlertData(DeviceDataVo deviceDataVo, AlertRule alertRule, List<Long> allConsumerIds) {
        String format = CharSequenceUtil.format("{}{}{},持续{}个周期就报警", alertRule.getFunctionName(), alertRule.getOperator(), alertRule.getValue(), alertRule.getDuration());
        AlertData alertData =  BeanUtil.toBean(deviceDataVo, AlertData.class);
        // 设置数据
        alertData.setAlertRuleId(alertRule.getId());
        alertData.setAlertReason(format);
        alertData.setType(alertRule.getAlertDataType());
        alertData.setStatus(0);

        // 插入报警数据
        List<AlertData> collect = allConsumerIds.stream().map(userId -> {
            AlertData bean = BeanUtil.toBean(alertData, AlertData.class);
            bean.setUserId(userId);
            return bean;
        }).collect(Collectors.toList());
        alertDataMapper.insertList(collect);

        return collect;
    }
}
